package com.appshelves.model;

import java.util.Iterator;
import java.util.List;
import java.util.Random;

import android.content.pm.ResolveInfo;

import com.appshelves.model.PackageChangesLoader.Change;
import com.appshelves.utils.ComponentHelper;
import com.appshelves.utils.Profiler;

class ModelCreator extends ModelAPI {

    private static final int EXCLUDES_NUMBER = 2;

    private static ModelCreator _instance;

    private boolean rebuildAppData;
    private boolean alreadyLoaded;

    void loadIfNeeded() {
        if (alreadyLoaded)
            return;
        alreadyLoaded = true;

        if (!tryLoad())
            build();
    }

    void save() {
        Profiler p = new Profiler(this, "saveApps");
        getAppData().save();
        getShelves().save();
        p.e();
    }

    private boolean tryLoad() {
        Profiler p = new Profiler(this, "loadApps");

        getAppData().load();

        if (!getShelves().load())
            return false;

        if (rebuildAppData)
            rebuildAppData();

        handlePackageChanges();
        p.e();
        return true;
    }

    ///

    private void handlePackageChanges() {
        Profiler p = new Profiler(this, "read changes");
        PackageChangesLoader changesLoader = new PackageChangesLoader();

        getAccessor().allocateOffShelfSpace(changesLoader.getChangesNumber(Change.ADDED));
        for (PackageChangesLoader.PackageChange packageChange : changesLoader.getChangesList()) {
            if (packageChange.isAdded()) {
                getModel().updateOrAddApp(packageChange.packageName, true); // package may have existed on previous run, so update, not add.
            } else
                getModel().deleteApp(packageChange.packageName, true);
        }
        p.e();
    }

    /// building 

    private void build() {
        Profiler p = new Profiler(this, "buildApps");

        Profiler pp = new Profiler(this, "queryApps");
        List<ResolveInfo> rawApps = ComponentHelper.getRawApps();
        pp.e();

        parseApps(rawApps);
        p.e();

        generateTestShelves(); // TODO remove generateTestShelves

    }

    private void parseApps(List<ResolveInfo> rawApps) {
        Profiler p = new Profiler(this, "parseApps");

        getAppData().ensureCapacity(rawApps.size() - EXCLUDES_NUMBER);
        getAccessor().createOffShelf(rawApps.size() - EXCLUDES_NUMBER);

        for (ResolveInfo info : rawApps)
            if (!excludeApp(info))
                getAccessor().addNewApp(info);

        getAccessor().sortOffShelf();
        p.e();
    }

    private boolean excludeApp(ResolveInfo info) {
        return ComponentHelper.isPackageSelf(info) || info.activityInfo.packageName.contains("com.android.settings");
    }

    /// 

    void rebuildAppData() {
        getAppData().clear();
        for (Iterator<Shelf> shelfIt = getShelves().iterator(); shelfIt.hasNext();) {
            Shelf shelf = shelfIt.next();
            for (Iterator<App> appIt = getAccessor().getShelfApps(shelf).iterator(); appIt.hasNext();) {
                App app = appIt.next();
                if (!app.exists()) {
                    appIt.remove();
                    if (shelf != getAccessor().getOffShelf() && getAccessor().isShelfEmpty(shelf))
                        shelfIt.remove();
                } else
                    app.recreateData();
            }
        }
    }

    private void markRebuildAppData() {
        rebuildAppData = true;
    }

    void bindAppDataIfLoaded(App app, int dataID) {
        if (dataID < 0 || dataID >= getAppData().size()) {
            markRebuildAppData();
            return;
        }
        getAccessor().bindAppDataOnLoad(app, dataID);
    }

    ///

    static ModelCreator instance() {
        if (_instance == null)
            _instance = new ModelCreator();
        return _instance;
    }

    /// debug

    private void generateTestShelves() {
        int numShelves = 14;
        Shelf testShelves[] = new Shelf[numShelves];
        for (int i = 0; i < numShelves; i++)
            testShelves[i] = getAccessor().addNewShelfToTop();
        Random rand = new Random();
        int numToPut = 125;
        List<App> apps = getAccessor().getOffShelfApps();
        for (int i = Math.min(apps.size() - 1, numToPut); i >= 0; i--) {
            getAccessor().addAppToShelf(testShelves[rand.nextInt(numShelves)], apps.get(i));
        }
    }

}
